<?php

namespace System\Core;

use \System\Core\Errors\Error;
use \System\Types\Object;
use \System\Types;

/**
 * Description of Hook
 *
 * @author elton.schivei
 */
class Hook extends Object
{
    /**
     * Called very early during system execution. Only the benchmark and hooks class have been loaded
     * at this point. No routing or other processes have happened.
     */

    const PRE_SYSTEM = 0;

    /**
     * Called immediately prior to any of your controllers being called. All base classes, routing,
     * and security checks have been done.
     */
    const PRE_CONTROLLER = 1;

    /**
     * Called immediately after your controller is instantiated, but prior to any method calls happening.
     */
    const POST_CONTROLLER_CONSTRUCTOR = 2;


    /**
     * Called immediately after your controller is fully executed.
     */
    const POST_CONTROLLER = 3;

    /**
     * Overrides the Output::display() function, used to send the finalized page to the web browser at
     * the end of system execution. This permits you to use your own display methodology. Note that you will
     * need to reference the Engineer superobject with $this->ENG = Engineer::GetInstance() and then the
     * finalized data will be available by calling Output::GetResult()
     */
    const DISPLAY_OVERRIDE = 4;

    /**
     * Enables you to call your own function instead of the Output::displayCache() function in the output
     * class. This permits you to use your own cache display mechanism.
     */
    const CACHE_OVERRIDE = 5;

    /**
     * Called after the final rendered page is sent to the browser, at the end of system execution after
     * the finalized data is sent to the browser.
     */
    const POST_SYSTEM = 6;

    private static $hooks = array(
        self::PRE_SYSTEM => array(),
        self::PRE_CONTROLLER => array(),
        self::POST_CONTROLLER_CONSTRUCTOR => array(),
        self::POST_CONTROLLER => array(),
        self::DISPLAY_OVERRIDE => array(),
        self::CACHE_OVERRIDE => array(),
        self::POST_SYSTEM => array()
    );

    function __construct()
    {
        parent::__construct();
    }

    public static final function AddHook($name, $type, $function, $construct_args = array(), $method_args = array())
    {
        if (self::PRE_SYSTEM > (int) $type || self::POST_SYSTEM < (int) $type)
            throw new Error("O tipo do hook é inválido.");

        self::$hooks[(int) $type][$name] = array('call' => $function, 'instance' => null,
            'args' => $construct_args, 'call_args' => $method_args, 'inprogress' => false);
    }

    public static final function AddHookFromConfig()
    {
        $itens = \System\Core\Config::Get(Config::HOOKS);

        if ($itens)
        {
            for ($i = 0; $i < 7; $i++)
            {
                $hooks = $itens->Get($i)->Valor;

                if (count($hooks))
                {
                    foreach ($hooks as $name => $hook)
                    {
                        $c = $hook['construct_args'] ? : array();
                        $m = $hook['method_args'] ? : array();

                        self::AddHook($name, $i, $hook['function'], $c, $m);
                    }
                }
            }
        }
    }

    public static final function HookProccess($type)
    {
        self::LoadInstances();

        switch ($type)
        {
            case self::PRE_SYSTEM:
            case self::PRE_CONTROLLER:
            case self::POST_CONTROLLER_CONSTRUCTOR:
            case self::POST_CONTROLLER:
            case self::DISPLAY_OVERRIDE:
            case self::CACHE_OVERRIDE:
            case self::POST_SYSTEM:
                if (!\count(self::$hooks[$type]))
                    return false;

                foreach (self::$hooks[$type] as $props)
                {
                    if ($props['instance'] && !$props['inprogress'])
                    {
                        \call_user_func_array(array($props['instance'], $props['call']), $props['call_args']);
                        $props['inprogress'] = true;
                    }
                }
                break;

            default:
                return false;
                break;
        }

        return true;
    }

    public static final function LoadInstances()
    {
        self::AddHookFromConfig();

        foreach (self::$hooks as $type => $hook)
        {
            foreach ($hook as $instanceName => $props)
            {
                if (!$props['instance'])
                {
                    $loaderInstance = Loader::GetInstance();

                    $loaderInstance->Load($instanceName);

                    $reflectionHook = new \ReflectionClass($instanceName);

                    $instance = $reflectionHook->newInstanceArgs($props['args']);

                    self::$hooks[$type][$instanceName]['instance'] = $instance;
                }
            }
        }
    }

}
